package cn.edu.scau.cmi.ema.service;

import cn.edu.scau.cmi.ema.base.CmiPagination;
import cn.edu.scau.cmi.ema.domain.*;
import cn.edu.scau.cmi.ema.service.base.CmiEmaService;
import cn.edu.scau.cmi.ema.util.CmiSqlUtil;
import org.hibernate.Session;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Projections;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.Query;
import java.util.*;
@Service("FoodbatchService")
@Transactional

public class FoodbatchServiceImpl extends CmiEmaService implements FoodbatchService {
	public FoodbatchServiceImpl() {
	}

	@Transactional
	public Foodbatch saveFoodbatch(Foodbatch foodbatch) {
		Set<Detect> existingDetectsForFoodbatch = new HashSet<Detect>();
		Set<Event> existingEventsForFoodbatch = new HashSet<Event>();
		Set<Foodbatchadditive> existingFoodbatchadditivesForFoodbatch = new HashSet<Foodbatchadditive>();
		Set<Foodbatchfiller> existingFoodbatchfillersForFoodbatch = new HashSet<Foodbatchfiller>();
		Set<Foodbatchmaterial> existingFoodbatchmaterialsForFoodbatch = new HashSet<Foodbatchmaterial>();

		Set<Detect> selectedDetectsForFoodbatch = foodbatch.getDetectsForFoodbatch();
		Set<Event> selectedEventsForFoodbatch = foodbatch.getEventsForFoodbatch();
		Set<Foodbatchadditive> selectedFoodbatchadditivesForFoodbatch = foodbatch.getFoodbatchadditivesForFoodbatch();
		Set<Foodbatchfiller> selectedFoodbatchfillersForFoodbatch = foodbatch.getFoodbatchfillersForFoodbatch();
		Set<Foodbatchmaterial> selectedFoodbatchmaterialsForFoodbatch = foodbatch.getFoodbatchmaterialsForFoodbatch();

		if (foodbatch.getId() == null) {
			Detectmethod detectmethod = detectmethodDAO.findDetectmethodById(foodbatch.getDetectmethod().getId());
			Food food = foodDAO.findFoodById(foodbatch.getFood().getId());
			Unit unit = unitDAO.findUnitById(foodbatch.getUnit().getId());

			Foodbatch existingFoodbatch = foodbatchDAO.findFoodbatchByPhysicalPrimaryKey(food.getId(),
					foodbatch.getDate());

			foodbatch.setDetectmethod(detectmethod);
			foodbatch.setFood(food);
			foodbatch.setUnit(unit);

			if (isEquals(foodbatch, existingFoodbatch)) {
				return existingFoodbatch;
			}

			foodbatch = foodbatchDAO.store(foodbatch);
			foodbatchDAO.flush();

			for (Detect relatedDetect : selectedDetectsForFoodbatch) {
				relatedDetect.setFoodbatch(foodbatch);
				detectDAO.store(relatedDetect);
			}
			for (Event relatedEvent : selectedEventsForFoodbatch) {
				relatedEvent.setFoodbatch(foodbatch);
				eventDAO.store(relatedEvent);
			}
			for (Foodbatchadditive relatedFoodbatchadditive : selectedFoodbatchadditivesForFoodbatch) {
				relatedFoodbatchadditive.setFoodbatch(foodbatch);
				foodbatchadditiveDAO.store(relatedFoodbatchadditive);
			}
			for (Foodbatchfiller relatedFoodbatchfiller : selectedFoodbatchfillersForFoodbatch) {
				relatedFoodbatchfiller.setFoodbatch(foodbatch);
				foodbatchfillerDAO.store(relatedFoodbatchfiller);
			}
			for (Foodbatchmaterial relatedFoodbatchmaterial : selectedFoodbatchmaterialsForFoodbatch) {
				relatedFoodbatchmaterial.setFoodbatch(foodbatch);
				foodbatchmaterialDAO.store(relatedFoodbatchmaterial);
			}
		} else {
			Foodbatch existingFoodbatch = foodbatchDAO.findFoodbatchByPrimaryKey(foodbatch.getId());

			existingDetectsForFoodbatch = existingFoodbatch.getDetectsForFoodbatch();
			existingEventsForFoodbatch = existingFoodbatch.getEventsForFoodbatch();
			existingFoodbatchadditivesForFoodbatch = existingFoodbatch.getFoodbatchadditivesForFoodbatch();
			existingFoodbatchfillersForFoodbatch = existingFoodbatch.getFoodbatchfillersForFoodbatch();
			existingFoodbatchmaterialsForFoodbatch = existingFoodbatch.getFoodbatchmaterialsForFoodbatch();

			Set<Detect> prepareDeleteDetectsForFoodbatch = new HashSet<Detect>();
			Set<Detect> prepareAddDetectsForFoodbatch = new HashSet<Detect>();

			prepareDeleteDetectsForFoodbatch.addAll(selectedDetectsForFoodbatch);
			prepareDeleteDetectsForFoodbatch.addAll(existingDetectsForFoodbatch);

			prepareAddDetectsForFoodbatch.addAll(selectedDetectsForFoodbatch);
			prepareAddDetectsForFoodbatch.addAll(existingDetectsForFoodbatch);

			prepareDeleteDetectsForFoodbatch.removeAll(selectedDetectsForFoodbatch);
			prepareAddDetectsForFoodbatch.removeAll(existingDetectsForFoodbatch);

			for (Detect relatedDetect : prepareAddDetectsForFoodbatch) {
				relatedDetect.setFoodbatch(foodbatch);
				detectDAO.store(relatedDetect);
			}

			for (Detect relatedDetect : prepareDeleteDetectsForFoodbatch) {
				relatedDetect.setFoodbatch(null);
				detectDAO.store(relatedDetect);
			}
			Set<Event> prepareDeleteEventsForFoodbatch = new HashSet<Event>();
			Set<Event> prepareAddEventsForFoodbatch = new HashSet<Event>();

			prepareDeleteEventsForFoodbatch.addAll(selectedEventsForFoodbatch);
			prepareDeleteEventsForFoodbatch.addAll(existingEventsForFoodbatch);

			prepareAddEventsForFoodbatch.addAll(selectedEventsForFoodbatch);
			prepareAddEventsForFoodbatch.addAll(existingEventsForFoodbatch);

			prepareDeleteEventsForFoodbatch.removeAll(selectedEventsForFoodbatch);
			prepareAddEventsForFoodbatch.removeAll(existingEventsForFoodbatch);

			for (Event relatedEvent : prepareAddEventsForFoodbatch) {
				relatedEvent.setFoodbatch(foodbatch);
				eventDAO.store(relatedEvent);
			}

			for (Event relatedEvent : prepareDeleteEventsForFoodbatch) {
				relatedEvent.setFoodbatch(null);
				eventDAO.store(relatedEvent);
			}
			Set<Foodbatchadditive> prepareDeleteFoodbatchadditivesForFoodbatch = new HashSet<Foodbatchadditive>();
			Set<Foodbatchadditive> prepareAddFoodbatchadditivesForFoodbatch = new HashSet<Foodbatchadditive>();

			prepareDeleteFoodbatchadditivesForFoodbatch.addAll(selectedFoodbatchadditivesForFoodbatch);
			prepareDeleteFoodbatchadditivesForFoodbatch.addAll(existingFoodbatchadditivesForFoodbatch);

			prepareAddFoodbatchadditivesForFoodbatch.addAll(selectedFoodbatchadditivesForFoodbatch);
			prepareAddFoodbatchadditivesForFoodbatch.addAll(existingFoodbatchadditivesForFoodbatch);

			prepareDeleteFoodbatchadditivesForFoodbatch.removeAll(selectedFoodbatchadditivesForFoodbatch);
			prepareAddFoodbatchadditivesForFoodbatch.removeAll(existingFoodbatchadditivesForFoodbatch);

			for (Foodbatchadditive relatedFoodbatchadditive : prepareAddFoodbatchadditivesForFoodbatch) {
				relatedFoodbatchadditive.setFoodbatch(foodbatch);
				foodbatchadditiveDAO.store(relatedFoodbatchadditive);
			}

			for (Foodbatchadditive relatedFoodbatchadditive : prepareDeleteFoodbatchadditivesForFoodbatch) {
				relatedFoodbatchadditive.setFoodbatch(null);
				foodbatchadditiveDAO.store(relatedFoodbatchadditive);
			}
			Set<Foodbatchfiller> prepareDeleteFoodbatchfillersForFoodbatch = new HashSet<Foodbatchfiller>();
			Set<Foodbatchfiller> prepareAddFoodbatchfillersForFoodbatch = new HashSet<Foodbatchfiller>();

			prepareDeleteFoodbatchfillersForFoodbatch.addAll(selectedFoodbatchfillersForFoodbatch);
			prepareDeleteFoodbatchfillersForFoodbatch.addAll(existingFoodbatchfillersForFoodbatch);

			prepareAddFoodbatchfillersForFoodbatch.addAll(selectedFoodbatchfillersForFoodbatch);
			prepareAddFoodbatchfillersForFoodbatch.addAll(existingFoodbatchfillersForFoodbatch);

			prepareDeleteFoodbatchfillersForFoodbatch.removeAll(selectedFoodbatchfillersForFoodbatch);
			prepareAddFoodbatchfillersForFoodbatch.removeAll(existingFoodbatchfillersForFoodbatch);

			for (Foodbatchfiller relatedFoodbatchfiller : prepareAddFoodbatchfillersForFoodbatch) {
				relatedFoodbatchfiller.setFoodbatch(foodbatch);
				foodbatchfillerDAO.store(relatedFoodbatchfiller);
			}

			for (Foodbatchfiller relatedFoodbatchfiller : prepareDeleteFoodbatchfillersForFoodbatch) {
				relatedFoodbatchfiller.setFoodbatch(null);
				foodbatchfillerDAO.store(relatedFoodbatchfiller);
			}
			Set<Foodbatchmaterial> prepareDeleteFoodbatchmaterialsForFoodbatch = new HashSet<Foodbatchmaterial>();
			Set<Foodbatchmaterial> prepareAddFoodbatchmaterialsForFoodbatch = new HashSet<Foodbatchmaterial>();

			prepareDeleteFoodbatchmaterialsForFoodbatch.addAll(selectedFoodbatchmaterialsForFoodbatch);
			prepareDeleteFoodbatchmaterialsForFoodbatch.addAll(existingFoodbatchmaterialsForFoodbatch);

			prepareAddFoodbatchmaterialsForFoodbatch.addAll(selectedFoodbatchmaterialsForFoodbatch);
			prepareAddFoodbatchmaterialsForFoodbatch.addAll(existingFoodbatchmaterialsForFoodbatch);

			prepareDeleteFoodbatchmaterialsForFoodbatch.removeAll(selectedFoodbatchmaterialsForFoodbatch);
			prepareAddFoodbatchmaterialsForFoodbatch.removeAll(existingFoodbatchmaterialsForFoodbatch);

			for (Foodbatchmaterial relatedFoodbatchmaterial : prepareAddFoodbatchmaterialsForFoodbatch) {
				relatedFoodbatchmaterial.setFoodbatch(foodbatch);
				foodbatchmaterialDAO.store(relatedFoodbatchmaterial);
			}

			for (Foodbatchmaterial relatedFoodbatchmaterial : prepareDeleteFoodbatchmaterialsForFoodbatch) {
				relatedFoodbatchmaterial.setFoodbatch(null);
				foodbatchmaterialDAO.store(relatedFoodbatchmaterial);
			}
			foodbatch = foodbatchDAO.store(foodbatch);
			foodbatchDAO.flush();
		}

		detectDAO.flush();
		eventDAO.flush();
		foodbatchadditiveDAO.flush();
		foodbatchfillerDAO.flush();
		foodbatchmaterialDAO.flush();
		return foodbatch;
	}

	public void deleteFoodbatch(Foodbatch foodbatch) {
		foodbatchDAO.remove(foodbatch);
		foodbatchDAO.flush();
	}

	@Transactional
	public void deleteFoodbatchs(Set<Integer> deleteFoodbatchsId) {
		Query query = foodbatchDAO.createNamedQuery("deleteFoodbatchsById", -1, -1);
		query.setParameter(1, deleteFoodbatchsId);
		query.executeUpdate();
	}

	@Transactional
	public Set<Foodbatch> loadFoodbatchs() {
		return foodbatchDAO.findAllFoodbatchs();

	}

	@Transactional
	public Set<Foodbatch> loadReferenceFoodbatchs(Set<Foodbatch> foodbatchs) {
		Iterator<Foodbatch> foodbatchsIterator = foodbatchs.iterator();
		Set<Foodbatch> referencedFoodbatchs = new HashSet<>();
		while (foodbatchsIterator.hasNext()) {
			Foodbatch currentFoodbatch = foodbatchsIterator.next();

			if (!currentFoodbatch.getDetectsForFoodbatch().isEmpty()) {
				referencedFoodbatchs.add(currentFoodbatch);
				continue;
			}

			if (!currentFoodbatch.getEventsForFoodbatch().isEmpty()) {
				referencedFoodbatchs.add(currentFoodbatch);
				continue;
			}

			if (!currentFoodbatch.getFoodbatchadditivesForFoodbatch().isEmpty()) {
				referencedFoodbatchs.add(currentFoodbatch);
				continue;
			}

			if (!currentFoodbatch.getFoodbatchfillersForFoodbatch().isEmpty()) {
				referencedFoodbatchs.add(currentFoodbatch);
				continue;
			}

			if (!currentFoodbatch.getFoodbatchmaterialsForFoodbatch().isEmpty()) {
				referencedFoodbatchs.add(currentFoodbatch);
				continue;
			}
		}
		return referencedFoodbatchs;
	}

	public Set<Foodbatch> loadFoodbatchsByIdSet(Set<Integer> foodbatchIdSet) {

		Iterator<Integer> foodbatchIdIterator = foodbatchIdSet.iterator();
		Set<Foodbatch> foodbatchSet = new HashSet<>();
		while (foodbatchIdIterator.hasNext()) {
			Integer id = foodbatchIdIterator.next();
			Foodbatch currentFoodbatch = foodbatchDAO.findFoodbatchById(id);
			foodbatchSet.add(currentFoodbatch);
		}
		return foodbatchSet;
	}

	@Transactional
	public List<Foodbatch> findAllFoodbatchs(Integer startResult, Integer maxRows) {
		return new java.util.ArrayList<Foodbatch>(foodbatchDAO.findAllFoodbatchs(startResult, maxRows));
	}

	@Transactional
	public Foodbatch findFoodbatchByPrimaryKey(Integer id) {
		return foodbatchDAO.findFoodbatchByPrimaryKey(id);
	}

	@Transactional
	public Integer countFoodbatchs() {
		return ((Long) foodbatchDAO.createQuerySingleResult("select count(o) from Foodbatch o").getSingleResult())
				.intValue();
	}

	@Transactional
	public Integer countFoodbatchs(CmiPagination page) {
		String sql = CmiSqlUtil.createCountHql(page, Foodbatch.class);
		return ((Long) foodbatchDAO.createQuerySingleResult(sql).getSingleResult()).intValue();
	}

	/**
	 * search for name with ajax
	 *
	 */
	@Transactional
	public Integer countFoodbatchsByName(String queryName) {
		Query query = foodbatchDAO.createNamedQuery("countFoodbatchsByName", -1, -1);
		query.setParameter(1, queryName);
		return new Long((long) query.getSingleResult()).intValue();
	}

	@Transactional
	public Integer countFoodbatchsByDetachedCriteria(CmiPagination page) {
		DetachedCriteria detachedCriteria = CmiSqlUtil.createDetachedCriteria(page, Foodbatch.class);
		Session session = (Session) entityManager.getDelegate();
		detachedCriteria.setProjection(Projections.rowCount());
		return new Long((long) detachedCriteria.getExecutableCriteria(session).uniqueResult()).intValue();
	}

	public Map<String, ?> loadFoodbatchObjectReferencedSet(Foodbatch foodbatch) {
		HashMap<String, Object> objectReferencedSetMap = new HashMap<>();
		if (foodbatch.getDetectmethod() != null) {

			Set<Integer> detectmethodIds = new HashSet<Integer>();
			detectmethodIds.add(foodbatch.getDetectmethod().getId());
			objectReferencedSetMap.put("detectmethodIds", detectmethodIds);
		}
		if (foodbatch.getFood() != null) {

			Set<Integer> foodIds = new HashSet<Integer>();
			foodIds.add(foodbatch.getFood().getId());
			objectReferencedSetMap.put("foodIds", foodIds);
		}
		if (foodbatch.getUnit() != null) {

			Set<Integer> unitIds = new HashSet<Integer>();
			unitIds.add(foodbatch.getUnit().getId());
			objectReferencedSetMap.put("unitIds", unitIds);
		}

		Set<Integer> detectsIds = new HashSet<Integer>();
		for (Detect obj : foodbatch.getDetectsForFoodbatch()) {

			detectsIds.add(obj.getId());
		}
		objectReferencedSetMap.put("detectsIds", detectsIds);
		Set<Integer> eventsIds = new HashSet<Integer>();
		for (Event obj : foodbatch.getEventsForFoodbatch()) {

			eventsIds.add(obj.getId());
		}
		objectReferencedSetMap.put("eventsIds", eventsIds);
		Set<Integer> foodbatchadditivesIds = new HashSet<Integer>();
		for (Foodbatchadditive obj : foodbatch.getFoodbatchadditivesForFoodbatch()) {

			foodbatchadditivesIds.add(obj.getId());
		}
		objectReferencedSetMap.put("foodbatchadditivesIds", foodbatchadditivesIds);
		Set<Integer> foodbatchfillersIds = new HashSet<Integer>();
		for (Foodbatchfiller obj : foodbatch.getFoodbatchfillersForFoodbatch()) {

			foodbatchfillersIds.add(obj.getId());
		}
		objectReferencedSetMap.put("foodbatchfillersIds", foodbatchfillersIds);
		Set<Integer> foodbatchmaterialsIds = new HashSet<Integer>();
		for (Foodbatchmaterial obj : foodbatch.getFoodbatchmaterialsForFoodbatch()) {

			foodbatchmaterialsIds.add(obj.getId());
		}
		objectReferencedSetMap.put("foodbatchmaterialsIds", foodbatchmaterialsIds);

		return objectReferencedSetMap;
	}

	public Map<String, ?> loadFoodbatchClassReferencedSet(Foodbatch foodbatch) {
		HashMap<String, Object> classReferencedSetMap = new HashMap<>();
		classReferencedSetMap.put("detectmethods", detectmethodDAO.findAllDetectmethods());
		classReferencedSetMap.put("foods", foodDAO.findAllFoods());
		classReferencedSetMap.put("units", unitDAO.findAllUnits());
		classReferencedSetMap.put("detects", detectDAO.findAllDetects());
		classReferencedSetMap.put("events", eventDAO.findAllEvents());
		classReferencedSetMap.put("foodbatchadditives", foodbatchadditiveDAO.findAllFoodbatchadditives());
		classReferencedSetMap.put("foodbatchfillers", foodbatchfillerDAO.findAllFoodbatchfillers());
		classReferencedSetMap.put("foodbatchmaterials", foodbatchmaterialDAO.findAllFoodbatchmaterials());

		return classReferencedSetMap;
	}

	public boolean isEquals(Foodbatch foodbatch, Object obj) {
		if (obj == foodbatch)
			return true;
		if (!(obj instanceof Foodbatch))
			return false;
		Foodbatch comparedFoodbatch = (Foodbatch) obj;
		if (!CheckPropertyEqual(foodbatch.getFood().getId(), comparedFoodbatch.getFood().getId())) {
			return false;
		}
		if (!CheckPropertyEqual(foodbatch.getDate(), comparedFoodbatch.getDate())) {
			return false;
		}
		return true;
	}

	public static <T> boolean CheckPropertyEqual(T originProperty, T comparedProperty) {
		// 基本类型经过参数传递后会自动装箱
		String[] className = originProperty.getClass().getName().split("\\.");
		if (className[0].equals("java") && className[1].equals("math")) {
			return ((Comparable) originProperty).compareTo(comparedProperty) == 0 ? true : false;
		} else {
			return originProperty.equals(comparedProperty);
		}
	}
}

